package org.teamh.gui.ingame;

import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.util.glu.GLU.gluPerspective;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.util.glu.GLU;
import org.teamh.gui.GUI;
import org.teamh.gui.ingame.effects.EffectManager;

public class IngameFunctions {

	private FloatBuffer modelviewMatrix = BufferUtils.createFloatBuffer(16);
	private FloatBuffer projectionMatrix = BufferUtils.createFloatBuffer(16);
	private IntBuffer viewport = BufferUtils.createIntBuffer(16);
	private FloatBuffer depthResultPicking = BufferUtils.createFloatBuffer(1);
	private FloatBuffer resultPicking = BufferUtils.createFloatBuffer(3);
    private	FloatBuffer colorPickingBuffer = BufferUtils.createFloatBuffer(3);
	
	private GUI gui;

	private float transformX;
	private float transformZ;
	private boolean mouseClick;
	
	private int sidebarWidth;
	
	private Camera camera;

	private Map map;
	private RecruitCreep buildObject;
	
	private int mouseX = -1;
	private int mouseY = -1;
	
	private boolean dragMap = false;
	private boolean firstPos = true;
	private float dragPosX;
	private float dragPosZ;
	
	private EffectManager effectManager;
	private boolean clickForUpgrade;
	private int forMouseReleaseX;
	private int forMouseReleaseY;
	
	private FloatBuffer position;
	
	public IngameFunctions(GUI gui) {
		this.gui = gui;
		camera = new Camera();
		map = new Map(this);
		buildObject=new RecruitCreep(map);
		effectManager = EffectManager.create(this);

		sidebarWidth = (int)(250f / 768 * gui.getHeight());
		
		initLights();
	}
	
	private void initLights() {
		position = BufferUtils.createFloatBuffer(4).put(new float[]{3, 3, 3, 1});
		FloatBuffer diffuse = BufferUtils.createFloatBuffer(4).put(new float[]{0.5f, 0.5f, 0.5f, 1.0f});
		FloatBuffer ambient = BufferUtils.createFloatBuffer(4).put(new float[]{0.5f, 0.5f, 0.5f, 1.0f});
		FloatBuffer ambientLightModel = BufferUtils.createFloatBuffer(4).put(new float[]{1, 1, 1, 1.0f});
		
		position.rewind();
		diffuse.rewind();
		ambient.rewind();
		ambientLightModel.rewind();
		glLight(GL_LIGHT1, GL_POSITION, position);
		glLight(GL_LIGHT1, GL_DIFFUSE, diffuse);
		glLight(GL_LIGHT1, GL_AMBIENT, ambient);
		glLightModel(GL_LIGHT_MODEL_AMBIENT, ambientLightModel);
		glEnable(GL_LIGHT1);
	}
	
	private void updateLight() {
		position.rewind();
		glLight(GL_LIGHT1, GL_POSITION, position);
	}
	
	public void init() {
		camera.init();
		
		dragPosX = 0;
		dragPosZ = 0;
		dragMap = false;
		firstPos = true;
		mouseX = -1;
		mouseY = -1;
		
		transformX = 0;
		transformZ = 0;
	}
	
	public void renderForPicking() {
		if(mouseX < 0 || mouseY < 0) {
			return;
		}
		
		preRender();
		
		camera.look();
		
		if(clickForUpgrade) {
			glPushMatrix();
			{
				translateMap();
				map.drawPickingMode();
			}
			glPopMatrix();
			colorPickingBuffer.rewind();
			glReadPixels(mouseX, mouseY, 1, 1, GL_RGB, GL_FLOAT, colorPickingBuffer);
			if(colorPickingBuffer.get(2) * 2 - 1 == - 1) {
				int pickX = (int)((colorPickingBuffer.get(0) * 2 - 1) * 16) - 1 + ((colorPickingBuffer.get(0) * 2 - 1) > 0 ? 2 : 0);
				int pickZ = (int)((colorPickingBuffer.get(1) * 2 - 1) * 16) - 1 + ((colorPickingBuffer.get(1) * 2 - 1) > 0 ? 2 : 0);
				map.checkCell(pickX, pickZ);
			}
		} else {
			glPushMatrix();
			{
				translateMap();
				map.renderForPicking();
			}
			glPopMatrix();
			
			if(dragMap) {
				pickForDrag(mouseX, mouseY);
			} else {
				geometryPick(mouseX, mouseY);
			}
		}
		
		postRender();
		
		clickForUpgrade = false;
		mouseClick=false;
		mouseX = -1;
		mouseY = -1;
	}
	
	public void render(float timeElapsed) {
		preRender();
		
		camera.look();
		translateMap();
		map.render(timeElapsed);
		
		updateLight();
		glEnable(GL_LIGHTING);
		
		map.drawTower();
		
		glDisable(GL_LIGHTING);
		
		effectManager.render(timeElapsed);
		
		postRender();
	}
	

	private void preRender() {
		glViewport(0, 0, gui.getWidth() - sidebarWidth, gui.getHeight());
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(45.0f, (gui.getWidth() - sidebarWidth) / (float) gui.getHeight(), 0.1f, 50.0f);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
	}
	
	private void postRender() {
		gui.reshape();
	}

	private void translateMap() {
		glTranslatef(transformX, 0, transformZ);
	}
	
	public void pickForDrag(int x, int y) {
		modelviewMatrix.rewind();
		projectionMatrix.rewind();
		viewport.rewind();
		depthResultPicking.rewind();
		resultPicking.rewind();

		glGetFloat(GL_MODELVIEW_MATRIX, modelviewMatrix);
		glGetFloat(GL_PROJECTION_MATRIX, projectionMatrix);
		glGetInteger(GL_VIEWPORT, viewport);
		glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT,depthResultPicking);
		GLU.gluUnProject(x, y, depthResultPicking.get(0), modelviewMatrix,projectionMatrix, viewport, resultPicking);
		
		if(Math.abs(resultPicking.get(1)) < 0.01) {
			if(firstPos) {
				dragPosX = resultPicking.get(0);
				dragPosZ = resultPicking.get(2);
			} else {
				float newPosX = resultPicking.get(0);
				float newPosZ = resultPicking.get(2);
				
				transformX = Math.max(-6.5f, Math.min(6.5f, transformX + (newPosX - dragPosX)));
				transformZ = Math.max(-6.5f, Math.min(6.5f, transformZ + (newPosZ - dragPosZ)));
				
				dragPosX = newPosX;
				dragPosZ = newPosZ;
			}
		}
		
		firstPos = false;
	}
	
	public void geometryPick(int x, int y) {

		modelviewMatrix.rewind();
		projectionMatrix.rewind();
		viewport.rewind();
		depthResultPicking.rewind();
		resultPicking.rewind();

		glGetFloat(GL_MODELVIEW_MATRIX, modelviewMatrix);
		glGetFloat(GL_PROJECTION_MATRIX, projectionMatrix);
		glGetInteger(GL_VIEWPORT, viewport);
		glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT,depthResultPicking);
		GLU.gluUnProject(x, y, depthResultPicking.get(0), modelviewMatrix,projectionMatrix, viewport, resultPicking);
		
		float resX = resultPicking.get(0) - transformX;
		float resZ = resultPicking.get(2) - transformZ;
		
		int pickX = (int)(resX * 16) - 1 + (resX > 0 ? 2 : 0);
		int pickZ = (int)(resZ * 16) - 1 + (resZ > 0 ? 2 : 0);
		
		if (pickX <= 16 && pickX >= -16 && pickZ <=16 && pickZ >= -16) {
			if(mouseClick){
				map.clickedField(pickX, pickZ);
				map.setBuildTower(true);
			}else{
				map.setPick(pickX, pickZ);
			}
		} else {
			map.unsetPick();
		}
	}

	public void mouseWheelMoved(int movement) {
		if(!gui.getIngameScreen().getIngameScreenHUD().getChatManager().isShowingChatbox()) {
			camera.mouseWheelMoved(movement);
		}
	}
	
	public GUI getGui() {
		return gui;
	}
	
	public void mousePressed(int posX, int posY, int button) {
		if(!gui.getIngameScreen().getIngameScreenHUD().getChatManager().isShowingChatbox()) {
				if(button == 1) {
					dragMap = true;
					firstPos = true;
					mouseX = posX;
					mouseY = posY;
					forMouseReleaseX = posX;
					forMouseReleaseY = posY;
				}else{
					if(gui.getIngameScreen().getIngameScreenHUD().getController().getTowerChoosed() == null) {
						clickForUpgrade = true;
					} else {
						clickForUpgrade = false;
					}
					mouseClick=true;
					mouseX = posX;
					mouseY = posY;
				
			}
			
		}
	}

	public void mouseReleased(int posX, int posY, int button) {
		dragMap = false;
		if(forMouseReleaseX == posX && forMouseReleaseY == posY && button == 1) {
			if(gui.getIngameScreen().getIngameScreenHUD().getController().getTowerChoosed() != null) {
				gui.getIngameScreen().getIngameScreenHUD().getController().setTowerChoosed(null);
				map.setModel_object(null);
				map.setTowerChoosed(false);
			} 
		}
	}

	public void mouseMoved(int posX, int posY, int button) {
		mouseX = posX;
		mouseY = posY;
	}

	public void mouseDragged(int posX, int posY, int button) {
		if(dragMap) {
			mouseX = posX;
			mouseY = posY;
		}
	}
	
	public Map getMap() {
		return map;
	}
	
	public RecruitCreep getRuncreep(){
		return buildObject;
	}

	public EffectManager getEffectManager() {
		return effectManager;
	}
	
	public FloatBuffer getModelviewMatrix() {
		return modelviewMatrix;
	}
}